using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Enuo.Dotnet.Api.Models;
using Enuo.Dotnet.Api.Service;
using Enuo.Dotnet.Api.Utils;
using Enuo.Dotnet.Core.Autowired;
using Enuo.Dotnet.Service;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;


namespace Enuo.Dotnet.Api
{
  public class Startup
  {

    public Startup(IConfiguration configuration)
    {
      Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    /// <summary>
    /// Autofac注册服务的地方,Autofac会自动调用
    /// </summary>
    /// <param name="containerBuilder"></param>
    public void ConfigureContainer(ContainerBuilder containerBuilder)
    {
      //var optionsBuilder = new DbContextOptionsBuilder<MySqlDbContext>();
      //optionsBuilder.UseMySQL(Configuration.GetConnectionString("MySqlDbContext"), b => b.MigrationsAssembly("Enuo.Dotnet.Web"));
      //containerBuilder.RegisterType<MySqlDbContext>()
      //                .AsSelf()
      //                .WithParameter("options", optionsBuilder.Options)
      //                .InstancePerLifetimeScope()
      //                .PropertiesAutowired(new AutowiredPropertySelector());

      //获取所有控制器类型并使用属性注入
      Type[] controllersTypeAssembly = typeof(Startup).Assembly.GetExportedTypes()
          .Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToArray();
      containerBuilder.RegisterTypes(controllersTypeAssembly).PropertiesAutowired(new AutowiredPropertySelector());

      //containerBuilder.RegisterType<OrderService>().As<IOrderService>();
      //批量自动注入,把需要注入层的程序集传参数
      containerBuilder.BatchAutowired(typeof(ArticleService).Assembly);
      containerBuilder.BatchAutowired(typeof(AuthenticateService).Assembly);
    }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
      //让控制器实例由容器创建
      services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
      services.AddDbContext<MySqlDbContext>(options => options.UseMySQL(Configuration.GetConnectionString("MySqlDbContext"), b => b.MigrationsAssembly("Enuo.Dotnet.Web")));
      //services.AddScoped<ArticleService>();
      //services.AddScoped<LoginVo>();
      //services.AddScoped(Assembly.GetAssembly(typeof(ArticleService)), "^Enuo.Dotnet.Service.*Service$");
      //services.AddScoped<IAuthenticateService, AuthenticateService>();

      services.Configure<JwtConfig>(Configuration.GetSection("JWTTokenconfig"));
      var token = Configuration.GetSection("JWTTokenconfig").Get<JwtConfig>();

      services.AddControllers();
      services.AddSwaggerGen(c =>
      {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "Enuo.Dotnet.Api", Version = "v1" });
        // c.OperationFilter<AddAuthTokenHeaderParameter>();

        //Bearer 的scheme定义
        var securityScheme = new OpenApiSecurityScheme()
        {
          Description = "JWT Authorization header using the Bearer scheme. Example: Authorization: Bearer { token} ",
          Name = "Authorization",
          //参数添加在头部
          In = ParameterLocation.Header,
          //使用Authorize头部
          Type = SecuritySchemeType.Http,
          //内容为以 bearer开头
          Scheme = "bearer",
          BearerFormat = "JWT"
        };

        //把所有方法配置为增加bearer头部信息
        var securityRequirement = new OpenApiSecurityRequirement
        {
          {
            new OpenApiSecurityScheme
            {
              Reference = new OpenApiReference
              {
                  Type = ReferenceType.SecurityScheme,
                  Id = "bearerAuth"
              }
            },
            new string[] {}
          }
        };

        //注册到swagger中
        c.AddSecurityDefinition("bearerAuth", securityScheme);
        c.AddSecurityRequirement(securityRequirement);

      });

      services.AddAuthentication(option =>
      {
        option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
      }).AddJwtBearer(option =>
      {
        option.RequireHttpsMetadata = false;
        option.SaveToken = true;
        option.TokenValidationParameters = new TokenValidationParameters
        {
          ValidateIssuer = false,//是否验证Issuer
          ValidateAudience = false,//是否验证Audience
          ValidateIssuerSigningKey = true,//是否验证SigningKey
          IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(token.Secret)),//拿到SigningKey
          ValidIssuer = token.Issuer,
          ValidAudience = token.Audience,
          ClockSkew = TimeSpan.FromMinutes(0)//设置缓冲时间，token的总有有效时间等于这个时间加上jwt的过期时间，如果不配置默认是5分钟
        };
      });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      //获取Autofac容器上下文信息
      IocManager.InitContainer(app.ApplicationServices.GetAutofacRoot());

      if (env.IsDevelopment())
      {
        app.UseDeveloperExceptionPage();
        //app.UseSwagger(c =>
        //{
        //  c.SerializeAsV2 = true;
        //});
        //app.UseSwaggerUI(c =>
        //{
        //  c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
        //});
        app.UseSwagger();
        app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Enuo.Dotnet.Api v1"));
      }

      app.UseHttpsRedirection();
      app.UseRouting();

      app.UseAuthentication();//认证
      app.UseAuthorization();//授权

      app.UseEndpoints(endpoints =>
      {
        endpoints.MapControllers();
      });
    }
  }
}
